// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package types

import "cmd/compile/internal/base"

// AlgKind描述了用于比较和散列类型的算法。
type AlgKind int

// go:生成stringer-type AlgKind-trima alg前缀。go 

const (
	// 运行时已知这些值。
	ANOEQ AlgKind = iota
	AMEM0
	AMEM8
	AMEM16
	AMEM32
	AMEM64
	AMEM128
	ASTRING
	AINTER
	ANILINTER
	AFLOAT32
	AFLOAT64
	ACPLX64
	ACPLX128

	// 类型可以作为常规内存进行比较/散列。
	AMEM AlgKind = 100

	// 类型需要特殊的比较/哈希函数。
	ASPECIAL AlgKind = -1
)

// AlgType返回用于比较和散列类型t的AlgKind。
// 如果返回ANOEQ，它还返回
// 使其不可比较的组件类型t。
func AlgType(t *Type) (AlgKind, *Type) {
	if t.Broke() {
		return AMEM, nil
	}
	if t.Noalg() {
		return ANOEQ, t
	}

	switch t.Kind() {
	case TANY, TFORW:
		// 将在稍后定义。
		return ANOEQ, t

	case TINT8, TUINT8, TINT16, TUINT16,
		TINT32, TUINT32, TINT64, TUINT64,
		TINT, TUINT, TUINTPTR,
		TBOOL, TPTR,
		TCHAN, TUNSAFEPTR:
		return AMEM, nil

	case TFUNC, TMAP:
		return ANOEQ, t

	case TFLOAT32:
		return AFLOAT32, nil

	case TFLOAT64:
		return AFLOAT64, nil

	case TCOMPLEX64:
		return ACPLX64, nil

	case TCOMPLEX128:
		return ACPLX128, nil

	case TSTRING:
		return ASTRING, nil

	case TINTER:
		if t.IsEmptyInterface() {
			return ANILINTER, nil
		}
		return AINTER, nil

	case TSLICE:
		return ANOEQ, t

	case TARRAY:
		a, bad := AlgType(t.Elem())
		switch a {
		case AMEM:
			return AMEM, nil
		case ANOEQ:
			return ANOEQ, bad
		}

		switch t.NumElem() {
		case 0:
			// 我们在上面检查了元素类型是否具有可比性。
			return AMEM, nil
		case 1:
			// 单元素数组与其单独的元素相同。
			return a, nil
		}

		return ASPECIAL, nil

	case TSTRUCT:
		fields := t.FieldSlice()

		// 一个字段结构与仅一个字段相同。
		if len(fields) == 1 && !fields[0].Sym.IsBlank() {
			return AlgType(fields[0].Type)
		}

		ret := AMEM
		for i, f := range fields {
			// 所有字段必须具有可比性。
			a, bad := AlgType(f.Type)
			if a == ANOEQ {
				return ANOEQ, bad
			}

			// 空白字段、填充字段、具有非内存的字段
			// 等式需要特殊比较。
			if a != AMEM || f.Sym.IsBlank() || IsPaddedField(t, i) {
				ret = ASPECIAL
			}
		}

		return ret, nil
	}

	base.Fatalf("AlgType: unexpected type %v", t)
	return 0, nil
}

// TypeHasNoAlg报告t是否没有任何相关的hash/eq 
// 算法，因为t或t的某些组件被标记为Noalg。
func TypeHasNoAlg(t *Type) bool {
	a, bad := AlgType(t)
	return a == ANOEQ && bad.Noalg()
}

// 可比较报告t是否为可比较类型。
func IsComparable(t *Type) bool {
	a, _ := AlgType(t)
	return a != ANOEQ
}

// ComparableField返回结构类型为t的不可比较字段（如果有）。
func IncomparableField(t *Type) *Field {
	for _, f := range t.FieldSlice() {
		if !IsComparable(f.Type) {
			return f
		}
	}
	return nil
}

// IsPaddedField报告结构类型t的第i个字段后面是否有填充。
func IsPaddedField(t *Type, i int) bool {
	if !t.IsStruct() {
		base.Fatalf("IsPaddedField called non-struct %v", t)
	}
	end := t.width
	if i+1 < t.NumFields() {
		end = t.Field(i + 1).Offset
	}
	return t.Field(i).End() != end
}
